package dao

import (
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	rentalpb "serve/rental/api/gen/v1"
	"serve/shared/id"
	mgutil "serve/shared/mongo"
	"serve/shared/mongo/objid"
)

const (
	tripField      = "trip"
	accountIdField = tripField + "." + "accountid"
	statusField    = tripField + "." + "status"
)

type Mongo struct {
	col *mongo.Collection
}

func NewMongo(db *mongo.Database) *Mongo {
	return &Mongo{
		col: db.Collection("trip"),
	}
}

type TripRecord struct {
	mgutil.ObjIDField     `bson:"inline"`
	mgutil.UpdatedAtField `bson:"inline"`
	Trip                  *rentalpb.Trip `bson:"trip"`
}

//CreateTrip  创建行程
func (m *Mongo) CreateTrip(c context.Context, trip *rentalpb.Trip) (*TripRecord, error) {
	r := &TripRecord{
		Trip: trip,
	}

	r.ID = mgutil.NewObjID()
	r.UpdatedAt = mgutil.UpdatedAt()
	_, err := m.col.InsertOne(c, r)
	if err != nil {
		return nil, err
	}
	return r, nil
}

//GetTrip 获取行程
func (m *Mongo) GetTrip(ctx context.Context, id id.TripID, accountID id.AccountID) (*TripRecord, error) {
	objId, err := objid.FormID(id)
	if err != nil {
		return nil, fmt.Errorf("invalid id: %v", err)
	}
	res := m.col.FindOne(ctx, bson.M{
		mgutil.IDFieldName: objId,
		accountIdField:     accountID,
	})

	if err := res.Err(); err != nil {
		return nil, err
	}

	var tr TripRecord
	err = res.Decode(&tr)
	if err != nil {
		return nil, err
	}
	return &tr, nil
}

//GetTrips 获取全部行程
func (m *Mongo) GetTrips(ctx context.Context, accountID id.AccountID, status rentalpb.TripStatus) ([]*TripRecord, error) {
	filter := bson.M{
		accountIdField: accountID.String(),
	}

	if status != rentalpb.TripStatus_IN_NOT_SPECIFIED {
		filter[statusField] = status
	}

	find, err := m.col.Find(ctx, filter)
	if err != nil {
		return nil, err
	}

	if err := find.Err(); err != nil {
		return nil, err
	}

	var trs []*TripRecord
	for find.Next(ctx) {
		var tr TripRecord
		err := find.Decode(&tr)
		if err != nil {
			continue
		}
		trs = append(trs, &tr)
	}

	return trs, nil
}

//UpdateTrip 更新
func (m *Mongo) UpdateTrip(ctx context.Context, tid id.TripID, aid id.AccountID, updatedAt int64, trip *rentalpb.Trip) error {
	objId, err := objid.FormID(tid)
	if err != nil {
		return fmt.Errorf("invalid id:%v", err)
	}

	newUpdatedAt := mgutil.UpdatedAt()
	res, err := m.col.UpdateOne(ctx, bson.M{
		mgutil.IDFieldName:        objId,
		accountIdField:            aid,
		mgutil.UpdatedAtFieldName: updatedAt,
	}, mgutil.Set(bson.M{
		tripField:                 trip,
		mgutil.UpdatedAtFieldName: newUpdatedAt,
	}))

	if err != nil {
		return err
	}
	if res.ModifiedCount == 0 {
		return mongo.ErrNoDocuments
	}

	return nil
}
